package cn.cj.erp.modules.system.controller;

import cn.cj.erp.bo.system.SysDeptBo;
import cn.cj.erp.bo.system.SysRoleBo;
import cn.cj.erp.bo.system.SysUserBo;
import cn.cj.erp.common.excel.util.ExcelUtil;
import cn.cj.erp.common.exception.ServiceException;
import cn.cj.erp.common.security.security.utils.SecurityUtils;
import cn.cj.erp.common.utils.Assert;
import cn.cj.erp.common.utils.Convert;
import cn.cj.erp.common.utils.UserUtils;
import cn.cj.erp.common.vo.login.PasswordChangeVo;
import cn.cj.erp.modules.common.controller.AbstractController;
import cn.cj.erp.service.system.*;
import cn.cj.erp.common.vo.SysUserDetailVo;
import cn.cj.erp.common.constants.BizCodeEnum;
import cn.cj.erp.common.enums.StatusEnum;
import cn.cj.erp.common.security.annotation.Log;
import cn.cj.erp.common.enums.LogBusinessTypeEnum;
import cn.cj.erp.common.Page;
import cn.cj.erp.common.utils.ServletUtils;
import cn.cj.erp.common.http.Result;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
import java.util.StringJoiner;
import java.util.stream.Collectors;

/**
 * 系统用户
 *
 * @author chenjian
 */
@Validated
@Slf4j
@RestController
@RequestMapping("/system/user")
public class SysUserController extends AbstractController {
    @Resource
    private SysUserService sysUserService;
    @Resource
    private SysRoleService sysRoleService;
    @Resource
    private SysUserRoleService sysUserRoleService;
    @Resource
    private SysConfigService sysConfigService;
    @Resource
    private SysDeptService sysDeptService;
    
    /**
     * 所有用户列表
     */
    @PostMapping("/list")
    @PreAuthorize("@ss.hasPerm('system:user:list')")
    public Result<Page<SysUserBo>> list(@RequestBody SysUserBo params) {
        return Result.query(sysUserService.queryPage(params));
    }

    /**
     * 获取用户列表
     */
    @GetMapping("/listUserInfoByIds/{userIds}")
    @PreAuthorize("@ss.hasPerm('system:user:list')")
    public Result<List<SysUserBo>> listUserInfoByIds(@PathVariable("userIds") Long[] userIds) {
        return Result.query(sysUserService.listByIds(Arrays.asList(userIds)));
    }

    /**
     * 用户信息
     */
    @GetMapping(value = {"/info", "/info/{userId}"})
    @PreAuthorize("@ss.hasPerm('system:user:query')")
    public Result<SysUserDetailVo> info(@PathVariable(value = "userId", required = false) Long userId) {
        return Result.ok(getUserDetail(userId));
    }

    /**
     * 修改登录用户密码
     */
    @PostMapping("/password")
    @PreAuthorize("@ss.hasPerm('system:user:resetPwd')")
    @Log(title = "用户管理", businessType = LogBusinessTypeEnum.UPDATE)
    public Result<Boolean> password(@RequestBody PasswordChangeVo passwordChange) {
        Assert.notNull(passwordChange, "入参不能为空");
        passwordChange.decrypt();
        Assert.strongPass(passwordChange.getNewPassword(), "新密码必须包含数字、字母大小写、特殊字符，且超过8位");
        boolean update = sysUserService.updatePassword(passwordChange.getUserId(), passwordChange.getPassword(), passwordChange.getNewPassword());
        return Result.update(update);
    }

    /**
     * 保存用户
     */
    @PostMapping("/save")
    @PreAuthorize("@ss.hasPerm('system:user:add')")
    @Log(title = "用户管理", businessType = LogBusinessTypeEnum.INSERT)
    public Result<Boolean> save(@Valid @RequestBody SysUserBo sysUser) {
        return Result.save(sysUserService.save(sysUser));
    }

    /**
     * 修改用户
     */
    @PostMapping("/update")
    @PreAuthorize("@ss.hasPerm('system:user:edit')")
    @Log(title = "用户管理", businessType = LogBusinessTypeEnum.UPDATE, excludeParamNames = "password")
    public Result<Boolean> update(@Valid @RequestBody SysUserBo sysUser) {
        return Result.update(sysUserService.updateById(sysUser));
    }

    /**
     * 删除用户
     */
    @PostMapping("/delete/{userIds}")
    @PreAuthorize("@ss.hasPerm('system:user:remove')")
    @Log(title = "用户管理", businessType = LogBusinessTypeEnum.DELETE)
    public Result<Boolean> delete(@PathVariable("userIds") Long[] userIds) {
        if (userIds == null || userIds.length > 1) {
            return Result.error("只能选择一个用户删除");
        }
        if (SecurityUtils.isCurrentLogin(userIds[0])) {
            throw new ServiceException("不能删除自己");
        }
        return Result.remove(sysUserService.removeById(userIds[0]));
    }

    /**
     * 重置用户
     */
    @PostMapping("/reset/{userId}")
    @PreAuthorize("@ss.hasPerm('system:user:edit')")
    @Log(title = "用户管理", businessType = LogBusinessTypeEnum.RESET)
    public Result<Boolean> reset(@PathVariable("userId") Long userId) {
        if (SecurityUtils.isCurrentLogin(userId)) {
            throw new ServiceException("不能重置自己");
        }
        return Result.ok(sysUserService.resetUserById(userId));
    }

    /**
     * 锁定用户
     */
    @PreAuthorize("@ss.hasPerm('system:user:edit')")
    @Log(title = "用户管理", businessType = LogBusinessTypeEnum.LOCK)
    @PostMapping("/lock/{userId}")
    public Result<Boolean> lock(@PathVariable("userId") Long userId) {
        return Result.ok(sysUserService.updateStatusById(userId, StatusEnum.ABNORMAL));
    }

    /**
     * 解锁用户
     */
    @PreAuthorize("@ss.hasPerm('system:user:edit')")
    @Log(title = "用户管理", businessType = LogBusinessTypeEnum.UNLOCK)
    @PostMapping("/unlock/{userId}")
    public Result<Boolean> unlock(@PathVariable("userId") Long userId) {
        return Result.ok(sysUserService.updateStatusById(userId, StatusEnum.NORMAL));
    }

    /**
     * 状态修改
     */
    @PreAuthorize("@ss.hasPerm('system:user:edit')")
    @Log(title = "用户管理", businessType = LogBusinessTypeEnum.UPDATE)
    @PostMapping("/changeStatus")
    public Result<Boolean> changeStatus(@RequestBody SysUserBo user) {
        return Result.update(sysUserService.updateStatusById(user.getUserId(), user.getStatus()));
    }

    @Log(title = "用户管理", businessType = LogBusinessTypeEnum.EXPORT)
    @PreAuthorize("@ss.hasPerm('system:user:export')")
    @PostMapping("/export")
    public void export(@RequestBody SysUserBo params) {
        List<SysUserBo> list = sysUserService.queryList(params);
        ExcelUtil.exportExcel(SysUserBo.class, list, ServletUtils.getResponse(), "用户数据");
    }

    @PostMapping("/importTemplate")
    public void importTemplate() {
        ExcelUtil.template(SysUserBo.class, ServletUtils.getResponse(), "用户导入模板");
    }

    @Log(title = "用户管理", businessType = LogBusinessTypeEnum.IMPORT,
            excludeParamNames = {"password", "phone", "email"})
    @PreAuthorize("@ss.hasPerm('system:user:import')")
    @PostMapping("/importData")
    public Result<Boolean> importData(MultipartFile file, boolean updateSupport) {
        Assert.notNull(file, "请选择导入文件");
        try {
            InputStream inputStream = file.getInputStream();
            List<SysUserBo> userList = ExcelUtil.importExcel(SysUserBo.class, inputStream);

            importUser(userList, updateSupport);
            Result<Boolean> result = Result.ok();
            result.setMsg("恭喜你，数据已全部导入成功！");
            return result;
        } catch (Exception e) {
            String msg = StrUtil.isBlank(e.getMessage()) ? "导入失败" : e.getMessage();
            throw new ServiceException(msg, e);
        }
    }

    /**
     * 校验用户名是否唯一
     */
    @PreAuthorize("@ss.hasPerm('system:user:edit')")
    @GetMapping("/checkUsernameUnique")
    public Result<Boolean> checkUsernameUnique(SysUserBo sysUser) {
        if (!sysUserService.checkUsernameUnique(sysUser)) {
            return Result.error(BizCodeEnum.EXIST_USERNAME);
        }
        return Result.ok(true);
    }

    /**
     * 校验邮箱是否唯一
     */
    @PreAuthorize("@ss.hasPerm('system:user:edit')")
    @GetMapping("/checkEmailUnique")
    public Result<Boolean> checkEmailUnique(SysUserBo sysUser) {
        if (!sysUserService.checkEmailUnique(sysUser)) {
            return Result.error(BizCodeEnum.EXIST_EMAIL);
        }
        return Result.ok(true);
    }

    /**
     * 校验手机是否唯一
     */
    @PreAuthorize("@ss.hasPerm('system:user:edit')")
    @GetMapping("/checkPhoneUnique")
    public Result<Boolean> checkPhoneUnique(SysUserBo sysUser) {
        if (!sysUserService.checkPhoneUnique(sysUser)) {
            return Result.error(BizCodeEnum.EXIST_PHONE);
        }
        return Result.ok(true);
    }

    // ----------------------------------------------------------------
    /**
     * 用户信息，给用户信息修改页和详情页使用
     */
    private SysUserDetailVo getUserDetail(Long userId) {
        List<SysRoleBo> roles = sysRoleService.list();
        // 若ID为空，则为新增操作，直接返回角色即可。
        if (ObjectUtil.isNull(userId)) {
            log.info("获取用户信息结束： {}", roles);
            return new SysUserDetailVo(roles);
        }
        SysUserBo userBo = sysUserService.getById(userId);
        Assert.notNull(userBo, "用户不存在");
        userBo.setPassword(null);
        // 获取用户所属的角色列表
        List<Long> dbRoleIds = sysUserRoleService.listRoleIdsByUserId(userId);
        // 给前端使用，需要将Long列表转化成String列表
        List<String> roleIds = dbRoleIds.stream().map(Convert::toStr).collect(Collectors.toList());
        return new SysUserDetailVo(userBo, roleIds, roles);
    }

    /**
     * 导入用户数据
     * @param userList 用户列表
     * @param updateSupport true支持更新  false不支持更新
     */
    private void importUser(List<SysUserBo> userList, Boolean updateSupport) {
        if (CollectionUtil.isEmpty(userList)) {
            throw new ServiceException("导入用户数据不能为空！");
        }
        int failureNum = 0;
        StringJoiner fail = new StringJoiner("\n");

        String initPassword = sysConfigService.getValue("sys.user.initPassword");
        for (SysUserBo user : userList) {
            try {
                SysDeptBo sysDeptBo = sysDeptService.getById(user.getDeptId());
                if (sysDeptBo == null) {
                    fail.add(StrUtil.format("{}.{}部门不存在;", failureNum++, user.getUsername()));
                    continue;
                }
                SysUserBo db = sysUserService.getByUsername(user.getUsername());
                if (db == null) {
                    user.setPassword(initPassword);
                    boolean result = sysUserService.save(user);
                    if (!result) {
                        fail.add(StrUtil.format("{}.{}导入失败;", failureNum++, user.getUsername()));
                    }
                } else if (updateSupport) {
                    user.setUserId(db.getUserId());
                    user.setRoleIds(db.getRoleIds());
                    //不允许更新超级管理员
                    if (UserUtils.isAdmin(user.getUserId())) {
                        fail.add(StrUtil.format("{}.{}为管理员不允许更新;", failureNum++, user.getUsername()));
                        continue;
                    }
                    boolean result = sysUserService.updateById(user);
                    if (!result) {
                        fail.add(StrUtil.format("{}.{}更新失败;", failureNum++, user.getUsername()));
                    }
                } else {
                    fail.add(StrUtil.format("{}.{} 已存在;", failureNum++, user.getUsername()));
                }
            } catch (Exception e) {
                fail.add(StrUtil.format("{}.{}导入失败;", failureNum++, user.getUsername()));
                log.error("{}导入失败", user.getUsername(), e);
            }
        }
        if (failureNum > 0) {
            String msg = StrUtil.format("很抱歉，存在 {} 条导入失败数据！\n", failureNum) + fail;
            throw new ServiceException(msg);
        }
    }
}
